﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MS.InformationManagement.DAL.RecordCenter;
using MS.InformationManagement.Core.RecordSiteQuery;
using MS.InformationManagement.Support.Constants;
using Microsoft.SharePoint;
using MS.InformationManagement.Support;
using MS.InformationManagement.DAL.DocumentCenter;
using MS.InformationManagement.DAL.DTOs;
using MS.InformationManagement.DAL.Support.EqualityComparers;

namespace MS.InformationManagement.BLL.Helper
{
    public class RecursiveHelper
    {
        /// <summary>
        /// Gets the whole descendant sections collection from a parent section.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="includeParent">if set to <c>true</c> [include parent].</param>
        /// <returns></returns>
        public static List<Seccion> GetDescendantSections(Seccion parent, bool includeParent)
        {
            string recorsSiteUrl = ConfigurationHelper.GetInstance().ConfigItems[GlobalConstants.SITE_RECORDS_URL];
            List<Seccion> descendants = new List<Seccion>();

            if (includeParent) { descendants.Add(parent); }

            using (SPSite site = new SPSite(recorsSiteUrl))
            {
                IEnumerable<Seccion> inmediateChilds = QueryRecordsSiteHelper<Seccion>.QueryRecordsChildEntitiesSite(GlobalConstants.LIST_BIBLIOTECA_SECCION
                    , parent.Id.ToString(), CustomSiteColumns.FIELD_SECCION_PADRE_INTERNAL_NAME, site);
                if (inmediateChilds != null)
                {
                    descendants.AddRange(inmediateChilds);
                    foreach (Seccion currentChild in inmediateChilds)
                    {
                        descendants.AddRange(GetDescendantSections(currentChild, site));
                    }
                }
            }

            return descendants;
        }

        /// <summary>
        /// Gets the descendant sections recursively.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="site">The site.</param>
        /// <returns></returns>
        private static List<Seccion> GetDescendantSections(Seccion parent, SPSite site)
        {
            List<Seccion> descendants = new List<Seccion>();

            IEnumerable<Seccion> inmediateChilds = QueryRecordsSiteHelper<Seccion>.QueryRecordsChildEntitiesSite(GlobalConstants.LIST_BIBLIOTECA_SECCION,
                parent.Id.ToString(), CustomSiteColumns.FIELD_SECCION_PADRE_INTERNAL_NAME, site);
            if (inmediateChilds != null)
            {
                descendants.AddRange(inmediateChilds);
                foreach (Seccion currentChild in inmediateChilds)
                {
                    descendants.AddRange(GetDescendantSections(currentChild, site));
                }
            }

            return descendants;
        }

        /// <summary>
        /// Gets the descendant sections name collection from a parent section.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="includeParent">if set to <c>true</c> [include parent].</param>
        /// <returns></returns>
        public static List<String> GetDescendantSectionsNames(Seccion parent, bool includeParent)
        {
            string recorsSiteUrl = ConfigurationHelper.GetInstance().ConfigItems[GlobalConstants.SITE_RECORDS_URL];
            List<String> descendants = new List<String>();

            if (includeParent) { descendants.Add(parent.Title); }

            using (SPSite site = new SPSite(recorsSiteUrl))
            {
                IEnumerable<Seccion> inmediateChilds = QueryRecordsSiteHelper<Seccion>.QueryRecordsChildEntitiesSite(GlobalConstants.LIST_BIBLIOTECA_SECCION
                    , parent.Id.ToString(), CustomSiteColumns.FIELD_SECCION_PADRE_INTERNAL_NAME, site);
                if (inmediateChilds != null)
                {
                    descendants.AddRange(from child in inmediateChilds select child.Title);
                    foreach (Seccion currentChild in inmediateChilds)
                    {
                        descendants.AddRange(GetDescendantSectionsNames(currentChild, site));
                    }
                }
            }

            return descendants;
        }

        /// <summary>
        /// Gets the descendant sections names recursively.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="site">The site.</param>
        /// <returns></returns>
        private static List<String> GetDescendantSectionsNames(Seccion parent, SPSite site)
        {
            List<String> descendants = new List<String>();

            IEnumerable<Seccion> inmediateChilds = QueryRecordsSiteHelper<Seccion>.QueryRecordsChildEntitiesSite(GlobalConstants.LIST_BIBLIOTECA_SECCION,
                parent.Id.ToString(), CustomSiteColumns.FIELD_SECCION_PADRE_INTERNAL_NAME, site);
            if (inmediateChilds != null)
            {
                descendants.AddRange(from child in inmediateChilds select child.Title);
                foreach (Seccion currentChild in inmediateChilds)
                {
                    descendants.AddRange(GetDescendantSectionsNames(currentChild, site));
                }
            }

            return descendants;
        }

        public static List<Seccion> GetParentSections(Seccion child, bool includeChild)
        {
            string recorsSiteUrl = ConfigurationHelper.GetInstance().ConfigItems[GlobalConstants.SITE_RECORDS_URL];
            List<Seccion> parents = new List<Seccion>();

            if (includeChild) { parents.Add(child); }

            if (child.SecciónPadre != null)
            {
                using (SPSite site = new SPSite(recorsSiteUrl))
                {
                    Seccion inmediateParent = QueryRecordsSiteHelper<Seccion>.QueryRecordsParentEntitySite(GlobalConstants.LIST_BIBLIOTECA_SECCION, child.SecciónPadre.Id.Value);
                    if (inmediateParent != default(Seccion))
                    {
                        parents.Add(inmediateParent);
                        parents.AddRange(GetParentSections(inmediateParent, site));
                    }
                }
            }

            return parents;
        }

        private static List<Seccion> GetParentSections(Seccion child, SPSite site)
        {

            List<Seccion> parents = new List<Seccion>();
            if (child.SecciónPadre != null)
            {
                Seccion inmediateParent = QueryRecordsSiteHelper<Seccion>.QueryRecordsParentEntitySite(GlobalConstants.LIST_BIBLIOTECA_SECCION, child.SecciónPadre.Id.Value, site);
                if (inmediateParent != default(Seccion))
                {
                    parents.Add(inmediateParent);
                    parents.AddRange(GetParentSections(inmediateParent, site));
                }
            }
            return parents;
        }

        /// <summary>
        /// Determines whether [is current user section admin].
        /// </summary>
        /// <returns>
        ///   <c>true</c> if [is current user section admin]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsCurrentUserSectionAdmin()
        {
            string currentUser = SPContext.Current.Web.CurrentUser.Name;
            return IsUserSectionAdmin(currentUser);
        }

        /// <summary>
        /// Determines whether [is user section admin] [the specified user].
        /// </summary>
        /// <param name="user">The user.</param>
        /// <returns>
        ///   <c>true</c> if [is user section admin] [the specified user]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsUserSectionAdmin(string user)
        {
            bool isSectionAdmin = false;
            string query = string.Format("<Where><Eq><FieldRef Name='UserInCharge' /><Value Type='User'>{0}</Value></Eq></Where>", user);
            SPQuery availabilityQuery = new SPQuery { Query = query };
            string recordSiteUrl = ConfigurationHelper.GetInstance().ConfigItems[GlobalConstants.SITE_RECORDS_URL];

            using (SPSite recordSite = new SPSite(recordSiteUrl))
            {
                SPList targetList = recordSite.RootWeb.Lists.TryGetList(GlobalConstants.LIST_BIBLIOTECA_SECCION);
                if (targetList == null) { throw new Exception("La lista de secciones no existe"); }
                SPListItemCollection foundElements = targetList.GetItems(availabilityQuery);
                isSectionAdmin = foundElements.Count > 0;
            }

            return isSectionAdmin;
        }

        /// <summary>
        /// Determines whether [is current user section admin].
        /// </summary>
        /// <returns>
        ///   <c>true</c> if [is current user section admin]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsCurrentUserSectionAdmin(SPSite site)
        {
            string currentUser = SPContext.Current.Web.CurrentUser.Name;
            return IsUserSectionAdmin(currentUser, site);
        }

        /// <summary>
        /// Determines whether [is user section admin] [the specified user].
        /// </summary>
        /// <param name="user">The user.</param>
        /// <returns>
        ///   <c>true</c> if [is user section admin] [the specified user]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsUserSectionAdmin(string user, SPSite site)
        {
            bool isSectionAdmin = false;
            string query = string.Format("<Where><Eq><FieldRef Name='UserInCharge' /><Value Type='User'>{0}</Value></Eq></Where>", user);
            SPQuery availabilityQuery = new SPQuery { Query = query };

            SPList targetList = site.RootWeb.Lists.TryGetList(GlobalConstants.LIST_BIBLIOTECA_SECCION);
            if (targetList == null) { throw new Exception("La lista de secciones no existe"); }
            SPListItemCollection foundElements = targetList.GetItems(availabilityQuery);
            isSectionAdmin = foundElements.Count > 0;


            return isSectionAdmin;
        }

        public static bool CurrentUserHasRole(string roleName)
        {
            string loginName = SPContext.Current.Web.CurrentUser.LoginName.GetUserWithOutDomain();
            return UserHasRole(loginName, roleName);
        }

        public static bool CurrentUserHasRole(string roleName, DCDocumentCenterDataContext context)
        {
            string loginName = SPContext.Current.Web.CurrentUser.LoginName.GetUserWithOutDomain();
            return UserHasRole(loginName, roleName, context);
        }

        public static bool UserHasRole(string loginName, string roleName)
        {
            bool userIsInRole = false;

            string currentSecurityProfile = PeopleHelper.GetPropertyUserProfile(GlobalConstants.UP_USER_PERFIL, loginName);
            using (DCDocumentCenterDataContext ctx = new DCDocumentCenterDataContext(SPContext.Current.Web.Url))
            {
                VincularPerfiles securityProfileEntity = ctx.Perfiles.SingleOrDefault(securityProfile => securityProfile.Perfil.Equals(currentSecurityProfile));
                if (securityProfileEntity == default(VincularPerfiles)) { throw new Exception(string.Format("El usuario {0} cuenta con un perfil con nombre {1} que no existe en la lista de perfiles.", loginName.GetUserWithOutDomain(), currentSecurityProfile.ToUpper())); }
                userIsInRole = securityProfileEntity.TipoRole.Count(role => role.Role.Equals(roleName)) > 0;
            }

            return userIsInRole;
        }

        public static bool UserHasRole(string loginName, string roleName, DCDocumentCenterDataContext context)
        {
            bool userIsInRole = false;
            string currentSecurityProfile = PeopleHelper.GetPropertyUserProfile(GlobalConstants.UP_USER_PERFIL, loginName);

            VincularPerfiles securityProfileEntity = context.Perfiles.SingleOrDefault(securityProfile => securityProfile.Perfil.Equals(currentSecurityProfile));
            if (securityProfileEntity == default(VincularPerfiles)) { throw new Exception(string.Format("El usuario {0} cuenta con un perfil con nombre {1} que no existe en la lista de perfiles.", loginName.GetUserWithOutDomain(), currentSecurityProfile.ToUpper())); }
            userIsInRole = securityProfileEntity.TipoRole.Count(role => role.Role.Equals(roleName)) > 0;


            return userIsInRole;
        }

        public static void PurgeUsers(ref List<UserDTO> users, string roleName)
        {
            string recordSiteUrl = ConfigurationHelper.GetInstance().ConfigItems[GlobalConstants.SITE_RECORDS_URL];
            List<UserDTO> usersToRemove = new List<UserDTO>();
            using (SPSite site = new SPSite(recordSiteUrl))
            {
                using (DCDocumentCenterDataContext ctx = new DCDocumentCenterDataContext(SPContext.Current.Web.Url))
                {
                    foreach (UserDTO user in users)
                    {
                        if (!UserHasRole(user.LoginName, GlobalConstants.ROLE_INFORM, ctx) && !IsUserSectionAdmin(user.DisplayName, site))
                        {
                            usersToRemove.Add(user);
                        }
                    }
                }
            }
            users = users.Except(usersToRemove).ToList();
        }

        public static void AddSectionOwner(ref List<UserDTO> users, string sectionOwnerName)
        {
            AddSectionOwner(ref users, sectionOwnerName, false);
        }

        public static void AddSectionOwner(ref List<UserDTO> users, string sectionOwnerName, bool includeCurrentUser)
        {
            SPUser sectionOwner = (from SPUser user in SPContext.Current.Web.Users
                                   where user.Name.Equals(sectionOwnerName)
                                   select user).SingleOrDefault();
            if (users.Count(user => user.LoginName.Equals(sectionOwner.LoginName.GetUserWithOutDomain())) == 0)
            {
                if (!includeCurrentUser && SPContext.Current.Web.CurrentUser.LoginName.Equals(sectionOwner.LoginName)) { return; }
                users.Add(
                    new UserDTO
                    {
                        LoginName = sectionOwner.LoginName,
                        DisplayName = sectionOwner.Name
                    });
            }

        }

        public static void AddSectionAdminUsers(ref List<UserDTO> users)
        {
            AddSectionAdminUsers(ref users, false);
        }

        public static void AddSectionAdminUsers(ref List<UserDTO> users, bool includeCurrentUser)
        {
            string query = string.Format("<Where><Neq><FieldRef Name='UserInCharge' /><Value Type='User'>{0}</Value></Neq></Where>", SPContext.Current.Web.CurrentUser.Name);
            SPUserCollection siteUsers = SPContext.Current.Web.Users;
            IEnumerable<Seccion> sections = null;
            sections = includeCurrentUser ? QueryRecordsSiteHelper<Seccion>.QueryAllRecords(GlobalConstants.LIST_BIBLIOTECA_SECCION, new Nullable<uint>())
                : QueryRecordsSiteHelper<Seccion>.QueryRecordsEntitiesSite(GlobalConstants.LIST_BIBLIOTECA_SECCION, query, new Nullable<uint>());
            var adminUsers = (from SPUser user in siteUsers
                              join section in sections
                                  on user.Name equals section.UsuarioEncargadoCorrespondencia
                              select new { LoginName = user.LoginName, Name = user.Name, Section = section.Title });
            foreach (var currentSectionAdminUser in adminUsers)
            {
                users.Add(
                    new UserDTO
                    {
                        LoginName = currentSectionAdminUser.LoginName,
                        DisplayName = string.Format("{0} - {1}", currentSectionAdminUser.Name, currentSectionAdminUser.Section)
                    });
            }
        }

        public static void AddSectionUsers(ref List<UserDTO> users, string sectionName)
        {
            Dictionary<string, string> sectionUsers = PeopleHelper.GetUsersByProperty(GlobalConstants.UP_DEPARTMENT, sectionName, false);
            var dependencyUsers = from user in sectionUsers
                    select new UserDTO { DisplayName = user.Value, LoginName = user.Key };
            dependencyUsers = dependencyUsers.Except(users, new UserDTOEqualityComparer());
            users.AddRange(dependencyUsers);
        }
    }
}
